﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using DG.Tweening;
using GameFrameWork;
using static GameFrameWork.Enums.Timer;

namespace GameFrameWork
{
    public class UIProgress : MonoBehaviour
    {
        public UISuperImage BG;

        public UISuperImage Progress;
        public Text Content;
        public GameObject Handler;

        public Action<PointerEventData> onDragEvt;

        Coroutine m_Coroutine;
        private TimeFormatType m_TimeType;
        private long m_TotalTime;
        private string m_Content;

        private float m_Value;
        private float m_MaxWidth;

        private TimeShowCount m_TimeShowCount;

        public float HandlerFactor = 1f;

        public float Value
        {
            get => m_Value;
            set
            {
                m_Value = value;
                Progress.fillAmount = m_Value;
                if (Handler != null)
                {
                    var tmpWidth = m_MaxWidth * HandlerFactor;
                    Handler.transform.SetLocalPositionX(tmpWidth * m_Value - tmpWidth / 2);
                }
            }
        }
        private Action m_SmoothCb;
        private Action m_EndCb;

        private string m_Text;

        public string text
        {
            get => m_Text;
            set
            {
                m_Text = value;
                if (!Content) return;
                Content.text = m_Text;
            }
        }

        public Text Text => Content;

        public RectTransform Rect { get; private set; }

        private void Awake()
        {
            InitFsm();

            Rect = transform.GetComponent<RectTransform>();
            m_MaxWidth = Rect.sizeDelta.x;
            if (m_Value > 0 && Handler != null)
            {
                var tmpWidth = m_MaxWidth * HandlerFactor;
                Handler.transform.SetLocalPositionX(tmpWidth * m_Value - tmpWidth / 2);
            }
        }

        public void InitDrag(Action<PointerEventData> dragEvt)
        {
            onDragEvt = dragEvt;
            if (Handler != null)
            {
                var img = Handler.GetComponentInChildren<Image>();
                if (img != null)
                {
                    var dragCmp = img.GetComponent<UIDragSupport>();
                    if (dragCmp != null)
                    {
                        dragCmp.onDragEvt = OnDrag;
                    }
                }
            }
        }

        public void OnDrag(PointerEventData eventData)
        {
            var worldPos = eventData.pointerCurrentRaycast.worldPosition;
            var p = Handler.transform.position;
            Handler.transform.SetPositionX(worldPos.x);
            var localX = Handler.transform.localPosition.x;
            var tmpWidth = m_MaxWidth * HandlerFactor;
            var val = (localX + tmpWidth / 2) / tmpWidth;
            Value = Mathf.Clamp01(val);
            onDragEvt?.Invoke(eventData);
        }

        private void OnDisable()
        {
            StopCountDown();
            StopProgressAnim();
        }

        /// <summary>
        /// ÉèÖÃ¿í¶ÈºÍ¸ß¶È
        /// </summary>
        /// <param name="vec2"></param>
        public void SetSizeDelta(Vector2 vec2)
        {
            Rect.sizeDelta = vec2;
            m_MaxWidth = Rect.sizeDelta.x;
        }

        /// <summary>
        /// ÓÃ¿í¶È»»Ëã³ö Value
        /// </summary>
        public void SetWidth(float width)
        {
            var val = width / m_MaxWidth;
            Value = val;
        }

        public void SetPro(long curValue, long maxValue)
        {
            Value = (float)curValue / maxValue;
            text = curValue.ToString() + "/" + maxValue.ToString();
        }

        public void SetProPercent(long curValue, long maxValue)
        {
            Value = (float)curValue / maxValue;
            text = curValue.ToString() + "%/" + maxValue.ToString() + "%";
        }

        /// <summary>
        /// ÉèÖÃÍ¼Æ¬¸ñÊ½
        /// </summary>
        /// <param name="type">Í¼Æ¬Ê¹ÓÃÀàÐÍ normal ,slice, fill µÈ</param>
        /// <param name="fillMethod">Ë®Æ½»¹ÊÇ´¹Ö±</param>
        /// <param name="fillOrigin">0 Left  1 Right</param>
        public void SetProgressImgType(Image.Type type, Image.FillMethod fillMethod, int fillOrigin)
        {
            Progress.type = type;
            Progress.fillMethod = fillMethod;
            Progress.fillOrigin = fillOrigin;
        }

        /// <summary>
        /// ÔOÖÃµ×ˆD
        /// </summary>
        /// <param name="path"></param>
        public void SetBgSprite(string path)
        {
            if (BG == null)
            {
                BG = GetComponent<UISuperImage>();
            }

            BG?.SetSprite(path);
        }

        /// <summary>
        /// ÔOÖÃßM¶ÈˆD
        /// </summary>
        /// <param name="path"></param>
        /// <param name="callback"></param>
        public void SetProgressSprite(string path, Action callback)
        {
            Progress.SetSprite(path, callback: callback);
        }

        public void SetTextColor(string colorHtml)
        {
            Content?.SetColor(colorHtml);
        }

        public void SetTextColor(Color color)
        {
            Content?.SetColor(color);
        }

        public void StopCountDown()
        {
            if (m_Coroutine != null)
            {
                StopCoroutine(m_Coroutine);
                m_Coroutine = null;
            }
        }

        public void CountDown(long totalTime, long leftTime, string content = "", Action completeCallback = null, TimeFormatType timeType = TimeFormatType.HH_MM_SS, TimeShowCount count = TimeShowCount.All)
        {
            m_TimeType = timeType;
            m_TotalTime = totalTime;
            m_Content = content;
            m_TimeShowCount = count;

            if (m_Coroutine != null)
            {
                StopCoroutine(m_Coroutine);
            }
            m_Coroutine = StartCoroutine(Utility.Coroutine.CountDown(leftTime, UpdateTimer, completeCallback));
        }

        private void UpdateTimer(float elapsedTime)
        {
            Value = elapsedTime == 0 ? 0 : elapsedTime / m_TotalTime;
            text = m_Content.IsNullOrEmpty() ? elapsedTime.TimeToString(m_TimeType, m_TimeShowCount) : m_Content.Format(elapsedTime.TimeToString(m_TimeType));
        }

        #region ProgressAnimation

        private const float FULL_SLIDER_TIME = 0.4f;
        private const float LAST_SLIDER_TIME = 0.6f;

        private float m_TargetValue = 0;
        private float m_StartValue = 0;
        private float m_AddValue = 0;
        private float m_SpaceTime = 0;
        Coroutine m_AniCoroutine;

        SimpleFSM m_Fsm = new SimpleFSM();
        SimpleState idle = new SimpleState("idle");
        SimpleState play = new SimpleState("play");
        private void InitFsm()
        {
            m_Fsm.AddState(idle, OnIdleEnter);
            m_Fsm.AddState(play, OnPlayEnter, OnPlayUpdate, OnPlayExit);
        }

        private void OnIdleEnter()
        {
            if (m_AddValue <= 0)
            {
                m_EndCb?.Invoke();
                m_EndCb = null;
                m_SmoothCb = null;
                StopAnimCoroutine();
                return;
            }
            m_Fsm.SetState(play);
        }

        private void OnPlayEnter()
        {
            if ((m_AddValue + m_StartValue) > 1)
            {
                m_TargetValue = 1;
                m_AddValue -= (m_TargetValue - m_StartValue);
                m_SpaceTime = (m_TargetValue - m_StartValue) * FULL_SLIDER_TIME;
            }
            else
            {
                m_TargetValue = m_AddValue;
                m_AddValue = 0;
                m_SpaceTime = (m_TargetValue - m_StartValue) * LAST_SLIDER_TIME;
            }
        }

        private void OnPlayUpdate(float dt)
        {
            float curValue = Mathf.Lerp(m_StartValue, m_TargetValue, m_Fsm.stateTime / m_SpaceTime);
            Value = Mathf.Min(curValue, m_TargetValue);
            if (m_Fsm.stateTime >= m_SpaceTime)
                m_Fsm.SetState(idle);
        }

        private void OnPlayExit()
        {
            m_StartValue = 0;
            if (m_AddValue > 1)
                m_SmoothCb?.Invoke();
        }

        /// <summary>
        /// ²¥·Å½ø¶ÈÌõ¶¯»­
        /// </summary>
        /// <param name="addValue"></param>
        /// <param name="cb"></param>
        /// <param name="endCb"></param>
        public void PlaySmoothProgress(float addValue, Action cb, Action endCb)
        {
            m_AniCoroutine = StartCoroutine(Utility.Coroutine.Update(OnUpdate));
            m_SmoothCb = cb;
            m_EndCb = endCb;
            m_AddValue += addValue;
            if (m_Fsm.IsState(play))
                return;
            m_StartValue = m_Value;
            m_Fsm.SetState(idle, true);
        }

        private void OnUpdate()
        {
            m_Fsm.UpdateState(Time.deltaTime);
        }

        private void StopProgressAnim()
        {
            m_AddValue = 0;
            m_Fsm.SetState(idle);
            StopAnimCoroutine();
        }

        private void StopAnimCoroutine()
        {
            if (m_AniCoroutine != null)
            {
                StopCoroutine(m_AniCoroutine);
                m_AniCoroutine = null;
            }
        }


        #endregion

        /// <summary>
        /// 填充Tweem
        /// </summary>
        /// <param name="endValue"></param>
        /// <param name="startValue"></param>
        /// <param name="durTime"></param>
        /// <param name="delayTime"></param>
        /// <param name="callback"></param>
        public void DOFillAmount(float endValue, float startValue = 0, float durTime = 0.5f, float delayTime = 0f, Action callback = null)
        {
            this.Progress.DOKill();
            Value = startValue;
            this.Progress.DOFillAmount(endValue, durTime).
            SetDelay(delayTime).
            OnComplete(() =>
            {
                Value = endValue;
                callback?.Invoke();
            });
        }
    }
}


